#include <rtdef.h>
#include "dev_spi.h"
#include <rtdbg.h>
#include <rtthread.h>
#include "EEPROM.h"
#include "hc32_ll_clk.h"
#include "cpuHc32f460.h"
#include "hc32_ll_fcg.h"
#include "hc32_ll_pwc.h"
#include "hc32_ll_adc.h"
#include "hc32_ll_gpio.h"
#include "RTmempool.h"
#include "stdbool.h"
uint8_t g_byEEErrTimes[2];	//EEPROM存储器故障次数
//-----------------------------------------------
//函数功能: IIC读数据函数，内部函数
//
//参数: 	
//			I2CNo[in]		选择读出哪个芯片
//			Addr[in]		要读出的起始地址
//			Num[in]			要读出的数据长度
//			Buf[in/out]		要读出的数据
//          
//返回值: 	TRUE:读出正确	FALSE：读出错误
//
//备注:   
//-----------------------------------------------
static uint16_t I2CReadDev(uint8_t I2CNo, uint16_t Addr, uint16_t Num,uint8_t *Buf)
{
    rt_ssize_t len=0,PageLen=0, ReadLen=0;
    uint16_t TrueAddr=0;
    struct rt_i2c_msg msg[2];
    // 地址取整
	TrueAddr = (Addr / PAGE_SIZE) * PAGE_SIZE;

	// 第一个页中数据长度
	PageLen = PAGE_SIZE - (uint16_t)(Addr - TrueAddr);

	// 要写入数据的长度
	ReadLen = (short)Num;
    
    // 是否所有数据都在一个扇区内
	if( ReadLen > Num )
	{
		PageLen = Num;
		ReadLen -= PageLen;
	}
    
    TrueAddr = Addr;
    while(1)
    {
        LL_PERIPH_WE(LL_PERIPH_GPIO);
        msg[0].addr  = MEM_24LC25601_DEVADDR >> 1;  // 地址右移1位！:cite[5]
        msg[0].flags = RT_I2C_WR;  // 写标志
        msg[0].buf   = (uint8_t *)&TrueAddr;
        msg[0].len   = 2;
        
        msg[1].addr  = (MEM_24LC25601_DEVADDR ) >> 1;  // 地址右移1位！:cite[5]
        msg[1].flags = RT_I2C_RD ;  // 读标志
        msg[1].buf   = Buf;
        msg[1].len   = ReadLen;
        len = rt_i2c_transfer(i2c_ee_dev, msg, 2);
        LL_PERIPH_WP(LL_PERIPH_GPIO);
        
                // 观察是否写入完成
        ReadLen -= PageLen;
        if( ReadLen <= 0 )
        {
            // 已经写完
            break;
        }
        
        // 操作地址
        TrueAddr += PageLen;
        Buf += PageLen;
        
        // 计算长度
        if( ReadLen >= PAGE_SIZE )
        {
            PageLen = PAGE_SIZE;
        }
        else
        {
            PageLen = ReadLen;
        }
    }
    if( len !=0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void EEPROM_Write(uint8_t I2CNo, uint16_t Addr, uint16_t Num, uint8_t *Buf)
{
    struct rt_i2c_msg msg[2];

    LL_PERIPH_WE(LL_PERIPH_GPIO);
    msg[0].addr  = MEM_24LC25601_DEVADDR >> 1;  // 地址右移1位！:cite[5]
    msg[0].flags = RT_I2C_WR;  // 写标志
    msg[0].buf   = (uint8_t *)&Addr;
    msg[0].len   = 2;
    
    msg[1].addr  = MEM_24LC25601_DEVADDR >> 1;  // 地址右移1位！:cite[5]
    msg[1].flags = RT_I2C_WR | RT_I2C_NO_START;  // 写标志
    msg[1].buf   = Buf;
    msg[1].len   = Num;
    
   rt_i2c_transfer(i2c_ee_dev, msg, 2);
    LL_PERIPH_WP(LL_PERIPH_GPIO);

}
//-----------------------------------------------
//函数功能: 向IIC设备某一页写数据，包括回读检测，内部函数
//
//参数: 	
//			ChipNo[in]		选择哪个芯片
//			Addr[in]		写数据的起始，肯定是页大小的整数倍
//			Len[in]			写数据的长度（不会超过一页大小）
//			pBuf[in/out]	写数据的缓冲
//          
//返回值: 	TRUE:写成功		FALSE:写失败
static uint16_t WriteXX24XXXPage(uint8_t ChipNo, uint16_t Addr, uint16_t Len, uint8_t * pBuf)
{
	// 器件地址
	uint8_t i;
	uint8_t * pReadBackPtr;
	bool Status;
	uint16_t wLen;

	// 写入数据
	EEPROM_Write(ChipNo, Addr, Len, pBuf);

	// 延时等待
	rt_thread_mdelay(6);

	// 申请回读缓冲
	wLen = Len;
	pReadBackPtr = (uint8_t *)rt_malloc(wLen );

	Status = FALSE;

	//等待回读
	for(i=0; i<5; i++)
	{
		if( i != 0 )
		{
			rt_thread_mdelay(1);
//            CLEAR_WATCH_DOG;
		}

		if( I2CReadDev(ChipNo, Addr, Len, pReadBackPtr) != TRUE )
		{
			continue;
		}

		// 检测是否写入
		if( memcmp(pBuf, pReadBackPtr, Len) == 0 )
		{
			Status = TRUE;
			break;
		}
	}

	// 释放缓冲
	rt_free(pReadBackPtr);

	// 返回结果
	return Status;
}
//-----------------------------------------------
//函数功能: 向IIC设备进行分页写数据处理，内部函数
//
//参数: 	
//			ChipNo[in]		选择哪个芯片
//			Addr[in]		写数据的起始
//			Len[in]			写数据的长度
//			pBuf[in/out]	写数据的缓冲
//          
//返回值: 	TRUE:写成功		FALSE:写失败
static uint16_t WriteMemWithPages(uint8_t ChipNo, uint16_t Addr, uint16_t Len, uint8_t * pBuf)
{
	volatile uint16_t TrueAddr;
	short WriteLen;
	uint16_t PageLen;
	uint8_t i;

	// 地址取整
	TrueAddr = (Addr / PAGE_SIZE) * PAGE_SIZE;

	// 第一个页中数据长度
	PageLen = PAGE_SIZE - (uint16_t)(Addr - TrueAddr);

	// 要写入数据的长度
	WriteLen = (short)Len;

	// 是否所有数据都在一个扇区内
	if( PageLen > Len )
	{
		PageLen = Len;
		WriteLen -= PageLen;
	}

	//对保护区域特殊处理
//	if( api_GetSysStatus(eSYS_STATUS_EN_WRITE_ENERGY) == FALSE )
//	{
//		if(CheckProtectArea( GET_SAFE_SPACE_ADDR( EnergySafeRom ), GET_SAFE_SPACE_ADDR( EnergySafeRom ) + sizeof(TEnergySafeRom), Addr, Addr + Len ) != TRUE)
//		{
//			api_WriteSysUNMsg(SYSUN_EEPROM_PROTECT_11);
//			return FALSE;
//		}
//	}

//	if( api_GetSysStatus(eSYS_STATUS_EN_WRITE_SAMPLEPARA) == FALSE )
//	{
//		if(CheckProtectArea( GET_SAFE_SPACE_ADDR( SampleSafeRom ), GET_SAFE_SPACE_ADDR( SampleSafeRom ) + sizeof(TSampleSafeRom), Addr, Addr + Len ) != TRUE)
//		{
//		    api_WriteSysUNMsg(SYSUN_EEPROM_PROTECT_33);
//			return FALSE;
//		}
//	}

	// 写入数据的起始地址
	TrueAddr = Addr;
	// 写入所有页
	for(;;)
	{
		// 重复写入，若成功或者重试次数到则跳出循环
		for(i=0; i<3; i++)
		{
			// 写入一个页
			if( WriteXX24XXXPage(ChipNo, TrueAddr, PageLen, pBuf) == TRUE )
			{
				if( ChipNo == CS_SPI_256401 )
				{
                    LOG_I("WRITE_EEPROM1_SUCCESSFUL");
//					api_ClrError(ERR_WRITE_EEPROM1);
//					g_byEEErrTimes[0] = 0;
				}
				else
				{
                    LOG_I("WRITE_EEPROM2_SUCCESSFUL");
//					api_ClrError(ERR_WRITE_EEPROM2);
//					g_byEEErrTimes[1] = 0;
				}
				break;
			}

			CLEAR_WATCH_DOG;
		}

		// 重试次数是否已到？
		if( i >= 3 )
		{
			//置错误标志
			if( ChipNo == CS_SPI_256401 )
			{
//				api_SetError( (ERR_WRITE_EEPROM1|0x8000) );
                LOG_E("ERR_WRITE_EEPROM1");
				if( g_byEEErrTimes[0] < 120 )
				{
					g_byEEErrTimes[0] ++;
					if( g_byEEErrTimes[0] == 120 )
					{
//						api_SetFollowReportStatus(eSTATUS_EEPROM_Error);//wyh   698文件中
					}
				}
			}
			else
			{

//				api_SetError( (ERR_WRITE_EEPROM2|0x8000) );
				if( g_byEEErrTimes[1] < 120 )
				{
					g_byEEErrTimes[1] ++;
					if( g_byEEErrTimes[1] == 120 )
					{
//						api_SetFollowReportStatus(eSTATUS_EEPROM_Error);//wyh   698文件中
					}
				}
			}
			// 大于最大重试次数，返回错误
			return FALSE;
		}

		// 观察是否写入完成
		WriteLen -= PageLen;
		if( WriteLen <= 0 )
		{
			// 已经写完
			break;
		}

		// 操作地址
		TrueAddr += PageLen;
		pBuf += PageLen;

		// 计算长度
		if( WriteLen >= PAGE_SIZE )
		{
			PageLen = PAGE_SIZE;
		}
		else
		{
			PageLen = WriteLen;
		}
	}

	return TRUE;
}
//-----------------------------------------------
//函数功能: 向IIC设备写数据，内部函数
//
//参数: 	
//			ChipNo[in]		选择哪个芯片
//			Addr[in]		写数据的起始
//			Len[in]			写数据的长度
//			pBuf[in/out]	写数据的缓冲
//          
//返回值: 	TRUE:写成功		FALSE:写失败
//
//备注:   
//-----------------------------------------------
uint16_t WriteXX24XXX(uint8_t ChipNo, uint16_t Addr, uint16_t Len, uint8_t * pBuf)
{
	uint8_t Result = false;

	// 判断写入的地址是否在24LC256的地址范围之内
//	ASSERT( (Addr+Len) <= SINGLE_CHIP_SIZE, 1 );
	if( (Addr+Len) > SINGLE_CHIP_SIZE )
	{
		return false;
	}
	
#if( SLAVE_MEM_CHIP == CHIP_NO )
//	if( ChipNo == CS_SPI_256402 )
//	{
//		Addr += SINGLE_CHIP_SIZE;
//		ChipNo = CS_SPI_256401;
//	}
#endif
	
	CLEAR_WATCH_DOG;
	
//	DoEEPRomProtect(ChipNo,FALSE);//写保护

	Result = WriteMemWithPages(ChipNo, Addr, Len, pBuf);

//	DoEEPRomProtect(ChipNo,TRUE);//写保护

  	return Result;
}




//-----------------------------------------------
//函数功能: 从IIC设备读数据，内部函数
//
//参数: 	
//			ChipNo[in]		选择哪个芯片
//			Addr[in]		读数据的起始
//			Len[in]			读数据的长度
//			pBuf[in/out]	读数据的缓冲
//          
//返回值: 	TRUE:读成功		FALSE:读失败
//
//备注:   
//-----------------------------------------------
uint16_t ReadXX24XXX(uint8_t ChipNo, uint16_t Addr, uint16_t Len, uint8_t * pBuf)
{
  	uint8_t Result,i;

	Result = TRUE;

	// 判断写入的地址是否在24LC256的地址范围之内
	if( (Addr+Len) > SINGLE_CHIP_SIZE )
	{
		return FALSE;
	}
	
	#if( SLAVE_MEM_CHIP == CHIP_NO )
//	if( ChipNo == CS_SPI_256402 )
//	{
//		Addr += SINGLE_CHIP_SIZE;
//		ChipNo = CS_SPI_256401;
//	}
	#endif

	// 读数据时不再打开写允许功能确保在读数据的时候不会产生误写操作
	for( i=0; i<3; i++ )
	{
//		CLEAR_WATCH_DOG;
		if( I2CReadDev(ChipNo, Addr, Len, pBuf) == TRUE )
		{
			break;
		}

        CLEAR_WATCH_DOG;
	}

	if( i >= 3 )
	{
		Result = FALSE;
	}

  	return Result;
}

CMD_OPTIONS_STATEMENT(Eeprom_Operate)
static int Eeprom_Operate(int argc, char **argv)
{
    uint32_t Reg_Len=0, Read_Len=0;
    uint32_t Rev_addr=0;
    int ReadNo=0;
    int i=0, j=0, k=0;
    rt_uint8_t Revbuf[1124], RevbufString[100], Rev_addrBuf[10],Rev_lenBuf[10] ;
    memset(RevbufString, 0, sizeof(RevbufString));

    if( (argc ==4) && (strcmp(argv[1], "read")== 0))
    {
        if( strlen(argv[2]) > 8 || strlen(argv[3]) > 8 )
        {
            rt_kprintf("addr or size len >8");
            return RT_ERROR;
        }
        
        StringToHexGroupBW((unsigned char *)&Rev_addr,argv[2],strlen(argv[2]));
        StringToHexGroupBW((unsigned char *)&Reg_Len,argv[3],strlen(argv[3]));
        
        do//for(i=0;; i++)
        {
            Read_Len = Reg_Len;
            if( Read_Len >1024)
            {
                Read_Len = ( Reg_Len > i*1024)? 1024 : Reg_Len %1024 ;
            }
            eeprom_Read_sync(Rev_addr + Read_Len*i, (uint8_t *)&Revbuf, Read_Len);
            

            for( k =0;k<Read_Len; k =k+16)
            {
                HexToAscii(Revbuf+k, RevbufString, 16);
                rt_kprintf("0x%x |", Rev_addr + Read_Len*i+k );
                for( j=0; j<16;j++)
                {
                    rt_kprintf("%-2s ", &RevbufString[j] );
                }
                rt_kprintf("\n");
            }  
            i++;
        }while( i< Reg_Len/1024);
    }
    else if(strcmp(argv[1], "erase") == 0)
    {
        rt_kprintf("禁止擦除 \r\n"); 
    }
    else if(strcmp(argv[1], "write") == 0)
    {
        
        rt_kprintf("禁止写 \r\n"); 
    }
    else
    {
        goto _usage;
    }

//    HexToAscii(Revbuf, RevbufString, Reg_Len);
//    rt_kprintf("当前 %-12s 为: 0x%-12s\n", EC_EMU_RegData[ReadNo].name, &RevbufString);
    return RT_EOK;
    
_usage:
    rt_kprintf("Usage: eeprom [options]\n");
    rt_kprintf("[options]:\n");
    rt_kprintf("eeprom erase \n"); 
    rt_kprintf("eeprom write \n"); 
    rt_kprintf("eeprom read  addr(0x) size(0x)\n"); 
    return RT_EOK;
}
CMD_OPTIONS_NODE_START(Eeprom_Operate)
CMD_OPTIONS_NODE(RT_Object_Class_Thread,       erase,       擦除 )
CMD_OPTIONS_NODE(RT_Object_Class_Thread,       write,       写   )
CMD_OPTIONS_NODE(RT_Object_Class_Thread,       read,        读   )
                                                                        
CMD_OPTIONS_NODE_END                                                    
MSH_CMD_EXPORT_ALIAS(Eeprom_Operate, eeprom, eeprom objects, optenable);